﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using DiscoveryLogic.Common.Utilities;
using DiscoveryLogic.Common.Utilities.DLR;
using DiscoveryLogic.DataViz.NetworkDiagram.LinkerControls;
using DiscoveryLogic.DataViz.NetworkDiagram.Measures;
using DiscoveryLogic.DataViz.NetworkDiagram.NetworkData;

namespace DiscoveryLogic.DataViz.NetworkDiagram
{
    /// <summary>
    /// 
    /// </summary>
    public class Graph
    {
        #region events
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fieldStatsByNetwork"></param>
        public delegate void OnFieldStatsUpdated(Dictionary<string,Dictionary<string,FieldStat>> fieldStatsByNetwork);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="g"></param>
        public delegate void OnGraphMeasureUpdated(Graph g);
        /// <summary>
        /// 
        /// </summary>
        public event OnFieldStatsUpdated FieldStatsUpdated;

        public event OnGraphMeasureUpdated GraphMeasureUpdated;
       
        #endregion

        #region props
        private Dictionary<Guid, Vertice> _Vertices;
        /// <summary>
        /// 
        /// </summary>
        public Dictionary<Guid, Vertice> Vertices
        {
            get { return _Vertices; }
            set { _Vertices = value; }
        }

        private Dictionary<Guid, Edge> _Edges;
        /// <summary>
        /// 
        /// </summary>
        public Dictionary<Guid, Edge> Edges
        {
            get { return _Edges; }
            set { _Edges = value; }
        }

        private Dictionary<string,Dictionary<string,FieldStat>> _NetworkDataFieldStats;
        /// <summary>network name -> member name -> field stat</summary>
        public Dictionary<string,Dictionary<string,FieldStat>> NetworkDataFieldStats
        {
            get
            {
                return this._NetworkDataFieldStats;
            }
            set
            {
                this._NetworkDataFieldStats = value;
            }
        }

        private Dictionary<string,Dictionary<string,LinkDataStat>> _NetworkLinkStats;
        /// <summary></summary>
        public Dictionary<string,Dictionary<string,LinkDataStat>> NetworkLinkStats
        {
            get
            {
                return this._NetworkLinkStats;
            }
            set
            {
                this._NetworkLinkStats = value;
            }
        }

        private Dictionary<string,Network> _Networks;
        /// <summary></summary>
        public Dictionary<string,Network> Networks
        {
            get
            {
                return this._Networks;
            }
            set
            {
                this._Networks = value;
            }
        }

        private List<NetworkJoin> _Joins;
        /// <summary></summary>
        public List<NetworkJoin> Joins
        {
            get
            {
                return this._Joins;
            }
            set
            {
                this._Joins = value;
            }
        }
        #endregion

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public Graph()
        {
            this._Vertices=new Dictionary<Guid, Vertice>();
            this._Edges=new Dictionary<Guid, Edge>();
            this._NetworkDataFieldStats=new Dictionary<string, Dictionary<string, FieldStat>>();
            this._NetworkLinkStats=new Dictionary<string, Dictionary<string, LinkDataStat>>();
            this._Networks=new Dictionary<string, Network>();
            this._Joins=new List<NetworkJoin>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="initialNetwork"></param>
        public void InitGraph(Network initialNetwork)
        {
            List<Vertice> vertices=new List<Vertice>();
            List<Edge> edges=new List<Edge>();
            this._Networks = new Dictionary<string, Network>();
            this._Joins = new List<NetworkJoin>();
            this._Networks.Add(initialNetwork.NetworkName, initialNetwork);
            DataTable dtNetData = new DataTable();
            List<LinkDataObject> linkDOs=new List<LinkDataObject>();
            Network.ReadData(initialNetwork.NetworkName, ref dtNetData, ref linkDOs);
            
            Dictionary<string, Vertice> pkVerticeMappings = new Dictionary<string, Vertice>();
            foreach (DataRow dr in dtNetData.Rows)
            {
                Dictionary<string,object> fieldValues=new Dictionary<string, object>();
                foreach(DataColumn col in dtNetData.Columns)
                {
                    fieldValues.Add(col.ColumnName, dr[col.ColumnName]);
                }
                string pkValue = fieldValues[initialNetwork.NetData.PkFieldName].ToString();
                string label = pkValue;
                if(!string.IsNullOrEmpty(initialNetwork.NetData.LabelFieldName) && 
                    fieldValues.ContainsKey(initialNetwork.NetData.LabelFieldName))
                {
                    label = fieldValues[initialNetwork.NetData.LabelFieldName].ToString();
                }
                Vertice v = new Vertice(Guid.NewGuid(), label, new PointF(), initialNetwork.NetworkName, pkValue);
                vertices.Add(v);
                pkVerticeMappings.Add(pkValue, v);
            }
            NetworkDataObject netDO = initialNetwork.NetData;
            foreach (LinkDataObject linkDO in linkDOs)
            {
                Vertice v1 = pkVerticeMappings[linkDO.FromKey];
                Vertice v2 = pkVerticeMappings[linkDO.ToKey];
                if(netDO.IsLinkDirectional)
                {
                    Arc e = new Arc(Guid.NewGuid(), linkDO.Distance.ToString(), (float)linkDO.Distance, v1.ID, v2.ID, linkDO);
                    edges.Add(e);
                }
                else
                {
                    Edge e = new Edge(Guid.NewGuid(), linkDO.Distance.ToString(), (float)linkDO.Distance, v1.ID, v2.ID, linkDO);
                    edges.Add(e);    
                }
            }
            
            this.InitializeGraphObjects(vertices, edges);
        }

        private void UpdateVerticeMeasures()
        {
            //Dictionary<Guid,Vertice> vClones=new Dictionary<Guid, Vertice>();
            //foreach(Vertice v in this._Vertices.Values)
            //{
            //    vClones.Add(v.ID, v.Clone());
            //}
            //Dictionary<Guid,Edge> eClones=new Dictionary<Guid, Edge>();
            //foreach(Edge e in this._Edges.Values)
            //{
            //    eClones.Add(e.ID, e.Clone());
            //}

            Dictionary<Guid, int> vDegrees = GraphMeasureWorker.GetVerticeDegrees(this.Vertices, this.Edges);
            Dictionary<Guid, int> vOutDegrees = GraphMeasureWorker.GetVerticeOutDegrees(this.Vertices, this.Edges);
            Dictionary<Guid, int> vInDegrees = GraphMeasureWorker.GetVerticeInDegrees(this.Vertices, this.Edges);
            Dictionary<Guid, double> vCloseCentralities =
                GraphMeasureWorker.CalculateVerticeCloseCentrality(this.Vertices, this.Edges);
            Dictionary<Guid, double> vBetweenCentralities =
                GraphMeasureWorker.CalculateVerticeBetweennessCentrality(this.Vertices, this.Edges);
            Guid[] vIDs=new Guid[this.Vertices.Count];
            this.Vertices.Keys.CopyTo(vIDs,0);
            foreach(Guid id in vIDs)
            {
                if (vDegrees.ContainsKey(id))
                    this.Vertices[id].Degree = vDegrees[id];
                else
                    this.Vertices[id].Degree = 0;
                if (vOutDegrees.ContainsKey(id))
                    this.Vertices[id].OutDegree = vOutDegrees[id];
                else
                    this.Vertices[id].OutDegree = 0;
                if (vInDegrees.ContainsKey(id))
                    this.Vertices[id].InDegree = vInDegrees[id];
                else
                    this.Vertices[id].InDegree = 0;
                if (vCloseCentralities.ContainsKey(id))
                    this.Vertices[id].CloseCentrality = vCloseCentralities[id];
                if (vBetweenCentralities.ContainsKey(id))
                    this.Vertices[id].BetweenCentrality = vBetweenCentralities[id];
            }

            if (this.GraphMeasureUpdated != null)
                this.GraphMeasureUpdated(this);
        }

        /// <summary>
        /// this method must be called synchronously
        /// </summary>
        /// <param name="vertices"></param>
        /// <param name="edges"></param>
        public void InitializeGraphObjects(List<Vertice> vertices, List<Edge> edges)
        {
            this._Vertices = new Dictionary<Guid, Vertice>();
            if (vertices != null && vertices.Count > 0)
            {
                foreach (Vertice v in vertices)
                {
                    this._Vertices.Add(v.ID, v);
                }
            }
            this._Edges = new Dictionary<Guid, Edge>();
            if (edges != null && edges.Count > 0)
            {
                foreach (Edge e in edges)
                {
                    this._Edges.Add(e.ID, e);
                }
            }

            this._NetworkDataFieldStats=new Dictionary<string, Dictionary<string, FieldStat>>();
            this._NetworkLinkStats=new Dictionary<string, Dictionary<string, LinkDataStat>>();
            foreach(string networkName in this.Networks.Keys)
            {
                DataTable dtNetData=new DataTable();
                List<LinkDataObject> netLinks=new List<LinkDataObject>();
                Network.ReadData(networkName, ref dtNetData, ref netLinks);
                Dictionary<string, FieldStat> fieldStats = FieldStatsUtil.GetFieldStats(dtNetData);
                this._NetworkDataFieldStats.Add(networkName, fieldStats);
                Dictionary<string,LinkDataStat> selfLinkStat=new Dictionary<string, LinkDataStat>();
                selfLinkStat.Add(networkName, LinkDataStat.ReadLinkStat(netLinks));
                this._NetworkLinkStats.Add(networkName, selfLinkStat);
            }
            if (this.FieldStatsUpdated != null)
                this.FieldStatsUpdated(this._NetworkDataFieldStats);
            //if(this.Joins !=null && this.Joins.Count>0)
            //{
            //    foreach(NetworkJoin join in this.Joins)
            //    {
            //        List<LinkDataObject> crossNetworkLinks=new List<LinkDataObject>();
            //        foreach(Edge e in this.Edges.Values)
            //        {
            //            if(e.Vertice1.NetworkName==join.FromNetworkName && e.Vertice2.NetworkName==join.ToNetworkName)
            //            {
            //                crossNetworkLinks.Add(e.Tag);
            //            }
            //            else if(e.Vertice1.NetworkName==join.ToNetworkName && e.Vertice2.NetworkName==join.FromNetworkName)
            //            {
            //                crossNetworkLinks.Add(e.Tag);
            //            }
            //        }
            //        Dictionary<string,LinkDataStat> crossLinkStat=new Dictionary<string, LinkDataStat>();
            //        crossLinkStat.Add(join.ToNetworkName, LinkDataStat.ReadLinkStat(crossNetworkLinks));
            //        this._NetworkLinkStats.Add(join.FromNetworkName, crossLinkStat);
            //    }
            //}
            
            this.UpdateVerticeMeasures();
            this.UpdateVisual();
        }
        #endregion

        #region visual settings
        /// <summary>
        /// using global visual settings to update 
        /// size, color, etc
        /// </summary>
        public void UpdateVisual()
        {
            Dictionary<string, NetworkVisualSetting> verticeVisualMappings = new Dictionary<string, NetworkVisualSetting>();
            foreach(Network net in this.Networks.Values)
            {
                if(!verticeVisualMappings.ContainsKey(net.NetworkName))
                {
                    verticeVisualMappings.Add(net.NetworkName, net.VizSetting);
                }
            }

            // v size 
            foreach(Vertice v in this._Vertices.Values)
            {
                if(verticeVisualMappings.ContainsKey(v.NetworkName))
                {
                    NetworkVisualSetting vizSetting = verticeVisualMappings[v.NetworkName];
                    // v size 
                    if(string.IsNullOrEmpty(vizSetting.VerticeSizeCode))
                    {
                        v.Width = vizSetting.VerticeSize.Width;
                        v.Height = vizSetting.VerticeSize.Height;
                    }
                    else
                    {
                        SizeF size = this.GetVerticeSize(v, vizSetting.VerticeSizeCode);
                        v.Width = size.Width;
                        v.Height = size.Height;
                    }
                    // v color 
                    if (string.IsNullOrEmpty(vizSetting.VerticeColorCode))
                    {
                        v.VerticeColorA = vizSetting.VerticeColor.A;
                        v.VerticeColorR = vizSetting.VerticeColor.R;
                        v.VerticeColorG = vizSetting.VerticeColor.G;
                        v.VerticeColorB = vizSetting.VerticeColor.B;
                    }
                    else
                    {
                        Color vColor = this.GetVerticeColor(v, vizSetting.VerticeColorCode);
                        v.VerticeColorA = vColor.A;
                        v.VerticeColorR = vColor.R;
                        v.VerticeColorG = vColor.G;
                        v.VerticeColorB = vColor.B;
                    }
                    // v shape
                    if(string.IsNullOrEmpty(vizSetting.VerticeShapeCode))
                    {
                        v.VerticeShape = vizSetting.VerticeShape;
                    }
                    else
                    {
                        v.VerticeShape = this.GetVerticeShape(v, vizSetting.VerticeShapeCode);
                    }
                }
            }

            foreach(Edge e in this._Edges.Values)
            {
                Vertice v1 = this._Vertices[e.VerticeID1];
                if (verticeVisualMappings.ContainsKey(v1.NetworkName))
                {
                    NetworkVisualSetting vizSetting = verticeVisualMappings[v1.NetworkName];
                    // e color 
                    if (string.IsNullOrEmpty(vizSetting.EdgeColorCode))
                    {
                        Color fixedColor = vizSetting.EdgeColor;
                        e.PenColor = fixedColor;
                    }
                    else
                    {
                        Color variableColor = this.GetEdgeColor(e, vizSetting.EdgeColorCode);
                        e.PenColor = variableColor;
                    }

                    // e thickness
                    if (string.IsNullOrEmpty(vizSetting.EdgeThicknessCode))
                    {
                        float fixedThickness = vizSetting.EdgeThickness;
                        e.Thickness = fixedThickness;
                    }
                    else
                    {
                        float variableThickness = this.GetEdgeThickness(e, vizSetting.EdgeThicknessCode);
                        e.Thickness = variableThickness;
                    }

                    // e dash style 
                    if(string.IsNullOrEmpty(vizSetting.EdgeLineStyleCode))
                    {
                        e.LineStyle = vizSetting.LineStyle;
                    }
                    else
                    {
                        EdgeLineStyle lineStyle = this.GetEdgeLineStyle(e, vizSetting.LineStyleCode);
                        e.LineStyle = lineStyle;
                    }
                }
            }
        }

        private Color GetEdgeColor(Edge e, string code)
        {
            List<string> globalAssemblies = new List<string>();
            List<string> localAssemblies = this.GetReferencedAssemblies();
            List<MethodArgument> methodArgs=new List<MethodArgument>();
            MethodArgument arg = new MethodArgument("edgeDistance", typeof(double), false, e.Length);
            methodArgs.Add(arg);
            MethodConstructInfo methodConstruct=new MethodConstructInfo(typeof(Color), "GetEdgeColor", methodArgs, code);
            object result = CodeDomUtil.ExecuteMethod(methodConstruct, globalAssemblies, localAssemblies);
            return (Color) result;
        }

        private VerticeShape GetVerticeShape(Vertice v, string code)
        {
            List<string> globalAssemblies = new List<string>();
            List<string> localAssemblies = this.GetReferencedAssemblies();
            List<MethodArgument> methodArgs = new List<MethodArgument>();
            MethodArgument arg = new MethodArgument("verticeDegree", typeof(int), false, v.Degree);
            methodArgs.Add(arg);
            MethodConstructInfo methodConstruct = new MethodConstructInfo(typeof(VerticeShape), "GetVerticeShape", methodArgs, code);
            object result = CodeDomUtil.ExecuteMethod(methodConstruct, globalAssemblies, localAssemblies);
            return (VerticeShape)result;
        }

        private Color GetVerticeColor(Vertice v, string code)
        {
            List<string> globalAssemblies=new List<string>();
            List<string> localAssemblies = this.GetReferencedAssemblies();
            List<MethodArgument> methodArgs = new List<MethodArgument>();
            MethodArgument arg = new MethodArgument("verticeDegree", typeof(int), false, v.Degree);
            methodArgs.Add(arg);
            MethodConstructInfo methodConstruct = new MethodConstructInfo(typeof(Color), "GetVerticeColor", methodArgs, code);
            object result = CodeDomUtil.ExecuteMethod(methodConstruct, globalAssemblies, localAssemblies);
            return (Color)result;
        }

        private float GetEdgeThickness(Edge e, string code)
        {
            List<string> globalAssemblies = new List<string>();
            List<string> localAssemblies = this.GetReferencedAssemblies();
            List<MethodArgument> methodArgs = new List<MethodArgument>();
            MethodArgument arg = new MethodArgument("edgeDistance", typeof(double), false, e.Length);
            methodArgs.Add(arg);
            MethodConstructInfo methodConstruct = new MethodConstructInfo(typeof(float), "GetEdgeThickness", methodArgs, code);
            object result = CodeDomUtil.ExecuteMethod(methodConstruct, globalAssemblies, localAssemblies);
            return (float)result;
        }

        private EdgeLineStyle GetEdgeLineStyle(Edge e, string code)
        {
            List<string> globalAssemblies = new List<string>();
            List<string> localAssemblies = this.GetReferencedAssemblies();
            List<MethodArgument> methodArgs = new List<MethodArgument>();
            MethodArgument arg = new MethodArgument("edgeDistance", typeof(double), false, e.Length);
            methodArgs.Add(arg);
            MethodConstructInfo methodConstruct = new MethodConstructInfo(typeof(EdgeLineStyle), "GetEdgeLineStyle", methodArgs, code);
            object result = CodeDomUtil.ExecuteMethod(methodConstruct, globalAssemblies, localAssemblies);
            return (EdgeLineStyle)result;
        }

        private SizeF GetVerticeSize(Vertice v, string code)
        {
            List<string> globalAssemblies = new List<string>();
            List<string> localAssemblies = this.GetReferencedAssemblies();
            List<MethodArgument> methodArgs = this.BuildArgumentList(v);
            MethodConstructInfo methodConstruct = new MethodConstructInfo(typeof(SizeF), "GetVerticeSize", methodArgs, code);
            object result = CodeDomUtil.ExecuteMethod(methodConstruct, globalAssemblies, localAssemblies);
            return (SizeF)result;
        }

        private List<string> GetReferencedAssemblies()
        {
            List<string> assemblyNames=new List<string>();
            string graphAssemblyFilePath = this.GetType().Assembly.Location;
            string dllFilePath = Path.Combine(
                Path.GetDirectoryName(graphAssemblyFilePath),
                Path.GetFileNameWithoutExtension(graphAssemblyFilePath) + ".dll");
            assemblyNames.Add(dllFilePath);
            
            return assemblyNames;
        }

        private List<MethodArgument> BuildArgumentList(Vertice v)
        {
            List<MethodArgument> args=new List<MethodArgument>();
            Network net = this.Networks[v.NetworkName];
            Dictionary<string, SimpleDataType> fieldDataTypes = net.NetData.FieldDataTypes;
            Dictionary<string, object> fieldValues = net.SelectByKey(v.PkValue);
            if (fieldValues != null && fieldValues.Count > 0)
            {
                foreach (string fieldName in fieldValues.Keys)
                {
                    SimpleDataType dataType = fieldDataTypes[fieldName];
                    if (dataType == SimpleDataType.ComplexType ||
                        dataType == SimpleDataType.StringListType ||
                        dataType == SimpleDataType.IntListType)
                        continue;
                    Type sysType = SimpleDataTypeUtil.ToSystemType(dataType);
                    if (dataType == SimpleDataType.EnumType)
                        sysType = typeof (int);
                    if(fieldValues[fieldName]!=null && fieldValues[fieldName]!=DBNull.Value)
                    {
                        MethodArgument arg = new MethodArgument(fieldName, sysType, false, fieldValues[fieldName]);
                        args.Add(arg);
                    }
                }
            }

            PropertyInfo[] vProps = typeof(Vertice).GetProperties();
            foreach (PropertyInfo prop in vProps)
            {
                SimpleDataType dataType = SimpleDataTypeUtil.ToSimpleDataType(prop.PropertyType);
                if (dataType == SimpleDataType.ComplexType ||
                    dataType == SimpleDataType.StringListType ||
                    dataType == SimpleDataType.IntListType)
                    continue;
                Type sysType = prop.PropertyType;
                if (dataType == SimpleDataType.EnumType)
                    sysType = typeof (int);

                MethodArgument arg = new MethodArgument(prop.Name, sysType, false, prop.GetValue(v, null));
                args.Add(arg);
            }

            return args;
        }
        #endregion

        #region update from network
        /// <summary>
        /// 
        /// </summary>
        /// <param name="network"></param>
        /// <param name="join"></param>
        public void JoinNetwork(Network network, NetworkJoin join)
        {
            if (this.Networks.ContainsKey(network.NetworkName))
                return;
            
            this._Networks.Add(network.NetworkName, network);
            this._Joins.Add(join);
            Assembly assembly = this.GetType().Assembly;
            Type joinCalcType = assembly.GetType(join.JoinCalcTypeName);
            INetworkJoinCalcuator joinCalc = (INetworkJoinCalcuator) Activator.CreateInstance(joinCalcType);
            List<Vertice> vertices = new List<Vertice>();
            vertices.AddRange(this._Vertices.Values);
            List<Edge> edges=new List<Edge>();
            edges.AddRange(this._Edges.Values);
            
            Network net1 = null;
            foreach (Network net in this.Networks.Values)
            {
                if (net.NetworkName == join.FromNetworkName)
                {
                    net1 = net;
                    break;
                }
            }
            if (net1 == null)
                throw new Exception("Unable to find network in graph for join");

            joinCalc.Join(net1, join.FromObjMemberName, network, join.ToObjMemberName, join.Behavior, ref vertices, ref edges);

            this.InitializeGraphObjects(vertices, edges);
        }
        #endregion

        #region xml 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="graphPath"></param>
        public void Save(string graphPath)
        {
            if(File.Exists(graphPath))
                File.Delete(graphPath);

            XmlDocument xDoc = new XmlDocument();
            xDoc.LoadXml("<Graph></Graph>");
            XmlNode root = xDoc.DocumentElement;
            if(this.Networks !=null && this.Networks.Count>0)
            {
                XmlNode networkNodes = XmlDataUtil.AddElement(ref xDoc, root, "Networks");
                foreach(Network network in this.Networks.Values)
                {
                    XmlNode networkNode = XmlDataUtil.AddElement(ref xDoc, networkNodes, "Network");
                    XmlDataUtil.UpdateAttribute(ref xDoc, networkNode, "NetworkName", network.NetworkName);
                }
            }

            if(this.Joins !=null && this.Joins.Count>0)
            {
                XmlNode joinNodes = XmlDataUtil.AddElement(ref xDoc, root, "Joins");
                foreach(NetworkJoin join in this.Joins)
                {
                    XmlNode joinNode = XmlDataUtil.AddElement(ref xDoc, joinNodes, "Join");
                    join.Save(ref xDoc, joinNode);
                }
            }

            if(this.Vertices !=null && this.Vertices.Count>0)
            {
                XmlNode vNodes = XmlDataUtil.AddElement(ref xDoc, root, "Vertices");
                foreach(Vertice v in this.Vertices.Values)
                {
                    XmlNode vNode = XmlDataUtil.AddElement(ref xDoc, vNodes, "Vertice");
                    v.Save(ref xDoc, vNode);
                }
            }
            if(this.Edges !=null && this.Edges.Count>0)
            {
                XmlNode edgeNodes = XmlDataUtil.AddElement(ref xDoc, root, "Edges");
                foreach(Edge e in this.Edges.Values)
                {
                    if(e is Arc)
                    {
                        XmlNode eNode = XmlDataUtil.AddElement(ref xDoc, edgeNodes, "Arc");
                        ((Arc)e).Save(ref xDoc, eNode);
                    }
                    else
                    {
                        XmlNode eNode = XmlDataUtil.AddElement(ref xDoc, edgeNodes, "Edge");
                        e.Save(ref xDoc, eNode);
                    }
                }
            }

            // graph measures


            xDoc.Save(graphPath);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="graphPath"></param>
        /// <param name="vertices"></param>
        /// <param name="edges"></param>
        /// <param name="networks"></param>
        /// <param name="joins"></param>
        public static void Read(string graphPath, 
            ref List<Vertice> vertices, ref List<Edge> edges,
            ref Dictionary<string,Network> networks, 
            ref List<NetworkJoin> joins)
        {
            // Graph graph=new Graph();
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(graphPath);
            XmlNode root = xDoc.DocumentElement;

            XmlNodeList networkNodes = root.SelectNodes("Networks/Network");
            if(networkNodes !=null && networkNodes.Count>0)
            {
                networks=new Dictionary<string, Network>();
                foreach(XmlNode networkNode in networkNodes)
                {
                    string networkName = XmlDataUtil.GetAttributeValue(networkNode, "NetworkName", "");
                    Network network = Network.ReadNetwork(networkName);
                    networks.Add(network.NetworkName, network);
                }
            }

            XmlNodeList joinNodes = root.SelectNodes("Joins/Join");
            if(joinNodes !=null && joinNodes.Count>0)
            {
                joins=new List<NetworkJoin>();
                foreach(XmlNode joinNode in joinNodes)
                {
                    NetworkJoin join = NetworkJoin.Read(joinNode);
                    joins.Add(join);
                }
            }

            vertices=new List<Vertice>();
            edges=new List<Edge>();
            XmlNodeList vNodes = root.SelectNodes("Vertices/Vertice");
            if(vNodes !=null && vNodes.Count>0)
            {
                foreach(XmlNode vNode in vNodes)
                {
                    Vertice v= new Vertice();
                    v = v.Read(vNode);
                    vertices.Add(v);
                }
            }
            XmlNodeList eNodes = root.SelectNodes("Edges/Edge");
            if(eNodes !=null && eNodes.Count>0)
            {
                foreach(XmlNode eNode in eNodes)
                {
                    Edge e = new Edge();
                    e = e.Read(eNode);
                    edges.Add(e);
                }
            }
            XmlNodeList arcNodes = root.SelectNodes("Edges/Arc");
            if (arcNodes != null && arcNodes.Count > 0)
            {
                foreach (XmlNode arcNode in arcNodes)
                {
                    Arc e = new Arc();
                    e = e.Read(arcNode);
                    edges.Add(e);
                }
            }
            
        }
        #endregion
    }
}
